/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.api.model;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Concrete version implementation consisting of a major, minor and patch number. See {@link IVersion} for
 * additional information about the semantic version system used by the Proteus Framework.
 *
 * @author Tacoma Four
 */
public class Version implements IVersion, Comparable<IVersion>
{
    private static final Logger logger = Logger.getLogger(Version.class.getCanonicalName());

    private Integer major;
    private Integer minor;
    private String patch;

    /**
     * Constructs an immutable, fully-qualified version stamp.
     *
     * @param major Major version
     * @param minor Minor version
     * @param patch Patch level
     */
    public Version(int major, int minor, String patch)
    {
        this.major = major;
        this.minor = minor;
        this.patch = patch;
    }

    /**
     * Constructs an immutable, partially-qualified version stamp that ignores patch level.
     * <p/>
     * Use this constructor to locate an API version that while safely ignores any new backwards compatible bug
     * fixes that may have been introduced.
     *
     * @param major Major version
     * @param minor Minor version
     */
    public Version(int major, int minor)
    {
        this(major, minor, null);
    }

    /**
     * Constructs an immutable, minimally-qualified version stamp that ignores minor version and patch level.
     * <p/>
     * Use this constructor to locate an API version that safely ignores any new backward compatible API
     * functionality that a minor upgrade may have introduced.
     *
     * @param major
     */
    public Version(int major)
    {
        this(major, -1, null);
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public int getMajor()
    {
        return major;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getMinor()
    {
        return minor;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPatch()
    {
        return patch;
    }

    /**
     * Equality is determined by comparing the major, minor, and patch level values against each other.
     * <p/>
     * To check for semantic (API) compatibility, <b>do not use this method</b>. Instead, use
     * {@link #isVersionCompatible(IVersion)}.
     *
     * @param o Object to check for equality
     * @return true, if the objects are equal
     */
    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Version version = (Version) o;

        if (!major.equals(version.major)) return false;
        if (minor != null ? !minor.equals(version.minor) : version.minor != null) return false;
        if (patch != null ? !patch.equals(version.patch) : version.patch != null) return false;

        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode()
    {
        int result = major.hashCode();
        result = 31 * result + minor.hashCode();
        result = 31 * result + (patch != null ? patch.hashCode() : 0);
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
        return "Version{" +
                "major=" + major +
                ", minor=" + minor +
                ((null != patch) ? ", patch=" + patch : "") +
                '}';
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public VersionCompatibility isVersionCompatible(IVersion version)
    {
        if (logger.isLoggable(Level.FINE))
            logger.fine("Performing compatibility check between my version, " +
                    this.toString() + " and argument version, " + version.toString());

        VersionCompatibility compatibility = VersionCompatibility.Undetermined;

        if (version.getMajor() == major)
        {
            if (logger.isLoggable(Level.FINE))
                logger.fine("Major version match");

            if (version.getMinor() != -1 && this.minor != -1)
            {
                compatibility = (this.minor >= version.getMinor())
                        ? VersionCompatibility.Compatible
                        : VersionCompatibility.Incompatible;
            } else if (version.getMinor() == -1)
            {
                compatibility = VersionCompatibility.Compatible;
            } else
            {
                compatibility = VersionCompatibility.Undetermined;
            }
        }

        if (logger.isLoggable(Level.FINE))
            logger.fine("Compatibility Determination: " + compatibility);

        return compatibility;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int compareTo(IVersion iVersion)
    {
        int result = 0;

        int majorCompare = major.compareTo(iVersion.getMajor());
        int minorCompare = minor.compareTo(iVersion.getMinor());
        int patchCompare = (null != patch) ? patch.compareTo(iVersion.getPatch()) : 0;

        if (majorCompare == 0)
        {
            if (minorCompare == 0)
            {
                result = patchCompare;
            } else
            {
                result = minorCompare;
            }
        } else
        {
            result = majorCompare;
        }

        return result;
    }
}
